#include "Precompiled.hpp"

template <class TemplatedClass>
Factory<TemplatedClass>::Factory() : currentID(1)
{}

template <class TemplatedClass>
Factory<TemplatedClass>::~Factory()
{}

template <class TemplatedClass>
void Factory<TemplatedClass>::Delete(TypelessHandle* target)
{
  DeleteElement(ConvertHandleToLocation(target));
}

template <class TemplatedClass>
bool Factory<TemplatedClass>::IsValid(const TypelessHandle* target) const
{
  unsigned int location = ConvertHandleToLocation(target);
  return (target->GetIndex() < indexToLocationVector.size() && location < Size() && IDVector[ConvertHandleToLocation(target)] == target->GetID());
}

template <class TemplatedClass>
void* Factory<TemplatedClass>::GetData(TypelessHandle* target)
{
  return reinterpret_cast<void*>(&data[ConvertHandleToLocation(target)]);
}

template <class TemplatedClass>
const void* Factory<TemplatedClass>::GetData(const TypelessHandle* target) const
{
  return reinterpret_cast<const void*>(&data[ConvertHandleToLocation(target)]);
}

template <class TemplatedClass>
unsigned int Factory<TemplatedClass>::Size() const
{
  return data.size();
}

template <class TemplatedClass>
void Factory<TemplatedClass>::Clear()
{
  availableIndeces.clear();
  indexToLocationVector.clear();
  locationToIndexVector.clear();
  IDVector.clear();
  data.clear();
}

template <class TemplatedClass>
TemplatedClass& Factory<TemplatedClass>::operator [] (unsigned int location)
{
  return data[location];
}

template <class TemplatedClass>
const TemplatedClass& Factory<TemplatedClass>::operator [] (unsigned int location) const
{
  return data[location];
}

template <class TemplatedClass>
Handle<TemplatedClass> Factory<TemplatedClass>::GetHandleFromLocation(unsigned int location)
{
  ASSERT (location < Size());
  return Handle<TemplatedClass>(this, ConvertLocationToIndex(location), IDVector[location]);
}

template <class TemplatedClass>
const Handle<TemplatedClass> Factory<TemplatedClass>::GetHandleFromLocation(unsigned int location) const
{
  ASSERT (location < Size());
  return Handle<TemplatedClass>(this, ConvertLocationToIndex(location), IDVector[location]);
}

template <class TemplatedClass>
Handle<TemplatedClass> Factory<TemplatedClass>::CreateNewObject()
{
  unsigned int index;
  unsigned int location = Size();
  if(!availableIndeces.empty())
  {
    index = availableIndeces[availableIndeces.size() -1];
    availableIndeces.pop_back();
    indexToLocationVector[index] = location;
  }
  else
  {
    index = indexToLocationVector.size();
    indexToLocationVector.push_back(location);
  }

  data.push_back(TemplatedClass());
  locationToIndexVector.push_back(index);
  IDVector.push_back(currentID);
  ++currentID;
  return Handle<TemplatedClass>(this, index, currentID -1);
}

template <class TemplatedClass>
unsigned int Factory<TemplatedClass>::ConvertIndexToLocation(unsigned int index) const
{
  return indexToLocationVector[index];
}

template <class TemplatedClass>
unsigned int Factory<TemplatedClass>::ConvertLocationToIndex(unsigned int location) const
{
  return locationToIndexVector[location];
}

template <class TemplatedClass>
unsigned int Factory<TemplatedClass>::ConvertHandleToLocation(const TypelessHandle* target) const
{
  return ConvertIndexToLocation(target->GetIndex());
}

template <class TemplatedClass>
void Factory<TemplatedClass>::SwapElements(unsigned int location1, unsigned int location2)
{
  if(location1 != location2)
  {
    std::swap(indexToLocationVector[ConvertLocationToIndex(location1)], indexToLocationVector[ConvertLocationToIndex(location2)]);
    std::swap(locationToIndexVector[location1], locationToIndexVector[location2]);
    std::swap(IDVector[location1], IDVector[location2]);
    std::swap(data[location1], data[location2]);
  }
}

template <class TemplatedClass>
void Factory<TemplatedClass>::SwapElementWithEnd(unsigned int location)
{
  SwapElements(location, Size() -1);
}

template <class TemplatedClass>
void Factory<TemplatedClass>::DeleteElement(unsigned int location)
{
  SwapElementWithEnd(location);
  DeleteEnd();
}

template <class TemplatedClass>
void Factory<TemplatedClass>::DeleteEnd()
{
  availableIndeces.push_back(ConvertLocationToIndex(Size() -1));
  locationToIndexVector.pop_back();
  IDVector.pop_back();
  data.pop_back();
}